package com.corpgovernment.organization.service;

import com.corpgovernment.api.organization.soa.resident.GetAllDifferentialPriceRequest;
import com.corpgovernment.api.organization.soa.resident.GetDifferentialPriceRequest;
import com.corpgovernment.api.organization.soa.resident.GetDifferentialPriceResponse;
import com.corpgovernment.api.travelstandard.enums.ControlTypeEnum;
import com.corpgovernment.api.travelstandard.enums.HotelManageRulesEnum;
import com.corpgovernment.api.travelstandard.enums.HotelSwitchEnum;
import com.corpgovernment.api.travelstandard.vo.*;
import com.corpgovernment.api.travelstandard.vo.request.GetHotelDetailRequest;
import com.corpgovernment.common.base.BaseService;
import com.corpgovernment.common.base.BaseUserInfo;
import com.corpgovernment.common.utils.BaseUtils;
import com.corpgovernment.common.utils.DateUtil;
import com.corpgovernment.organization.entity.db.MbOrgInfo;
import com.corpgovernment.travelstandard.enums.HotelTravelStanderFloatTypeEnum;
import com.corpgovernment.travelstandard.impl.AreaConfigService;
import com.corpgovernment.travelstandard.impl.MbTravelstandHotelPriceService;
import com.corpgovernment.travelstandard.impl.MbTravelstandHotelService;
import com.ctrip.corp.obt.generic.utils.CollectionUtils;
import com.ctrip.corp.obt.generic.utils.JsonUtils;
import com.ctrip.corp.obt.generic.utils.StringUtils;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;

@Slf4j
@Service
public class HotelResidentService extends BaseService {

    @Autowired
    private MbTravelstandHotelService travelstandHotelService;
    @Autowired
    @Qualifier(value = "defaultThreadPoolExecutor")
    private ThreadPoolExecutor defaultThreadPoolExecutor;
    @Autowired
    private AreaConfigService areaConfigService;
    @Autowired
    private IOrganizationInfoService organizationInfoService;
    @Autowired
    private MbTravelstandHotelPriceService travelstandHotelPriceService;

    /**
     *  计算同住人差标价格
     *
     * @param request
     * @param userInfo
     * @return
     */
    public GetDifferentialPriceResponse getDifferentialPrice(GetDifferentialPriceRequest request, BaseUserInfo userInfo) {
        try {
            initElkLog();
            if (CollectionUtils.isEmpty(request.getResidentList())) {
                addElkInfoLog("当前房间未添加入住人");
                return new GetDifferentialPriceResponse("0", "0", request.getRoomNumber(), "0", "0", Boolean.FALSE);
            }

            //判断当前是否关联出差申请单
            MbTravelstandHotelVo hotelSwitch = null;
            if (StringUtils.isEmpty(request.getPolicy()) || StringUtils.isEmpty(request.getPolicyOrgId())) {
                hotelSwitch = travelstandHotelService.getHotelSwitch(userInfo.getUid(), userInfo.getOrgId());
            } else {
                hotelSwitch = travelstandHotelService.getHotelSwitch(request.getPolicy(), request.getPolicyOrgId());
            }

            addElkInfoLog("当前同住配置数据: %s", JsonUtils.toJsonString(hotelSwitch));

            //查询出所有入住人的差标价格 上限及下限
            List<GetDifferentialPriceRequest.Resident> residentList = request.getResidentList();

            for (GetDifferentialPriceRequest.Resident resident : residentList) {
                if (StringUtils.isNotBlank(resident.getUid()) && StringUtils.isNotBlank(resident.getOrgId())) {
                    continue;
                }

                if (StringUtils.isNotBlank(request.getPolicy()) && StringUtils.isNotBlank(request.getPolicyOrgId())) {
                    resident.setUid(request.getPolicy());
                    resident.setOrgId(request.getPolicyOrgId());
                } else {
                    resident.setUid(userInfo.getUid());
                    resident.setOrgId(userInfo.getOrgId());
                }
            }

            List<HotelControlVo> list = Lists.newArrayList();
            GetDifferentialPriceResponse response = new GetDifferentialPriceResponse();
            for (GetDifferentialPriceRequest.Resident resident : residentList) {
                HotelControlVo vo = getHotelControl(resident.getUid(), resident.getOrgId(), request.getSDate(), request.getEDate(), request.getCityId());
                log.info("getDifferentialPrice.getHotelControl");
                if (vo == null && residentList.size() == 1) {
                    return GetDifferentialPriceResponse.getNotLimitVo();
                }
                if (vo == null && residentList.size() > 1) {
                    list.add(HotelControlVo.getNotLimitVo());
                    continue;
                }
                if ("0".equals(vo.getAveragePriceSet().getPriceCeiling()) && Objects.equals("0", vo.getAverageStarSet().getStarCeiling())) {
                    return GetDifferentialPriceResponse.getNotLimitVo();
                }
                if ("0".equals(vo.getAveragePriceSet().getPriceCeiling())) {
                    response.setIsLimit(Boolean.FALSE);
                }
                list.add(vo);
            }
            addElkInfoLog("房间人所有差价数据: %s", JsonUtils.toJsonString(list));
            //计算出当前房价的  上限及下限金额
            response = getPriceByType(list, hotelSwitch, request, response);
            return response;
        }catch (Exception e){
            log.error("查询入住人同住差标异常", e);
            throw  e;
        } finally {
            log.info("查询入住人同住差标:{}", getElkInfoLog());
            clearElkLog();
        }
    }

    /**
     *  根据同住类型计算出当前房间的差价金额
     *
     * @param list
     * @param hotelSwitch
     * @param request
     * @return
     */
    private GetDifferentialPriceResponse getPriceByType(List<HotelControlVo> list, MbTravelstandHotelVo hotelSwitch, GetDifferentialPriceRequest request, GetDifferentialPriceResponse response) {

        //当房间人数小于2时不计算同住差标直接返回当前配置差标
        if (list.stream().count() < 2) {
            HotelControlVo vo = list.get(0);
            return new GetDifferentialPriceResponse(
                    vo.getAveragePriceSet().getPriceCeiling(), vo.getAveragePriceSet().getPriceFloor(), request.getRoomNumber(),
                    vo.getAverageStarSet().getStarCeiling().getValue(), vo.getAverageStarSet().getStarFloor().getValue(), response.getIsLimit());
        }

        Double value = 0D;
        //如果差标就低，排除差标金额为零的如果还有其他数据，选金额最小的，如果没有的话，就选0，差标就高，如果金额存在0就选0（0代表不限）
        if (response.getIsLimit()) {
            if (Objects.equals(HotelManageRulesEnum.LOW.getCode(), hotelSwitch.getHotelManageRules())) {
                if (list.stream().filter((hotelControlVo -> "0".equals(hotelControlVo.getAveragePriceSet().getPriceCeiling()))).count() > 0) {
                    if (list.stream().filter((hotelControlVo -> !"0".equals(hotelControlVo.getAveragePriceSet().getPriceCeiling()))).count() > 0) {
                        value = list.stream()
                                .filter((hotelControlVo -> !"0".equals(hotelControlVo.getAveragePriceSet().getPriceCeiling())))
                                .map(hotelControlVo -> hotelControlVo.getAveragePriceSet().getPriceCeiling())
                                .collect(Collectors.toList()).stream().mapToDouble(Double::valueOf).min().getAsDouble();
                    }
                } else {
                    value = list.stream().map(hotelControlVo -> hotelControlVo.getAveragePriceSet().getPriceCeiling()).collect(Collectors.toList()).stream().mapToDouble(Double::valueOf).min().getAsDouble();
                }
            } else if (Objects.equals(HotelManageRulesEnum.TALL.getCode(), hotelSwitch.getHotelManageRules())) {
                if (list.stream().filter((hotelControlVo -> "0".equals(hotelControlVo.getAveragePriceSet().getPriceCeiling()))).count() == 0) {
                    value = list.stream().map(hotelControlVo -> hotelControlVo.getAveragePriceSet().getPriceCeiling()).collect(Collectors.toList()).stream().mapToDouble(Double::valueOf).max().getAsDouble();
                }
            } else if (Objects.equals(HotelManageRulesEnum.SUM.getCode(), hotelSwitch.getHotelManageRules())) {
                value = list.stream().map(hotelControlVo -> hotelControlVo.getAveragePriceSet().getPriceCeiling()).collect(Collectors.toList()).stream().mapToDouble(Double::valueOf).sum();
            }
        }
        //计算星级
        //星级最大  0为不限 为最大
        List<HotelControlVo> collect = list.stream().filter(a -> Objects.equals("0", a.getAverageStarSet().getStarCeiling().getValue())).collect(Collectors.toList());
        int maxasInt = 0;
        if (CollectionUtils.isEmpty(collect)) {
            maxasInt = list.stream().map(a -> a.getAverageStarSet().getStarCeiling()).mapToInt(a -> Integer.valueOf(a.getValue())).max().getAsInt();
        }

        int minasInt = list.stream().map(hotelControlVo -> hotelControlVo.getAverageStarSet().getStarFloor()).mapToInt(hotelControlVo -> Integer.valueOf(hotelControlVo.getValue())).min().getAsInt();

        return new GetDifferentialPriceResponse(
                BigDecimal.valueOf(value).divide(BigDecimal.valueOf(100L)).multiply(hotelSwitch.getSharedPercentage()).setScale(2, BigDecimal.ROUND_DOWN).toString(),
                String.valueOf(0),
                request.getRoomNumber(),
                String.valueOf(maxasInt),
                String.valueOf(minasInt),
                value == 0 ? false : response.getIsLimit());
    }

    /**
     *  差标查询差标
     *
     * @param uid
     * @param orgId
     * @param sDate
     * @param eDate
     * @param cityId
     * @return
     */
    private HotelControlVo getHotelControl(String uid, String orgId, String sDate, String eDate, String cityId) {
        GetHotelDetailRequest detailRequest = new GetHotelDetailRequest();
        detailRequest.setUid(uid);
        detailRequest.setOrgId(orgId);
        detailRequest.setCityCode(cityId);
        detailRequest.setStartDate(DateUtil.stringToDate(sDate, DateUtil.DF_YMD));
        detailRequest.setEndDate(DateUtil.stringToDate(eDate, DateUtil.DF_YMD));
        return travelstandHotelService.getHotelDetail(detailRequest);
    }

    /**
     *  多房间多人同住差标计算   做统一收口
     *
     * @param request
     * @param userInfo
     * @return
     */
    public HotelControlVo getAllDifferentialPrice(GetAllDifferentialPriceRequest request, BaseUserInfo userInfo) {
        try {
            initElkLog();
            if (CollectionUtils.isEmpty(request.getRooms())) {
                return new HotelControlVo();
            }
            List<Future<GetDifferentialPriceResponse>> futures = Lists.newArrayList();

            for (GetAllDifferentialPriceRequest.Room room : request.getRooms()) {
                for (GetDifferentialPriceRequest.Resident resident : room.getResidentList()) {
                    if (StringUtils.isNotBlank(resident.getUid()) && StringUtils.isNotBlank(resident.getOrgId())) {
                        continue;
                    }
                    if (StringUtils.isNotBlank(request.getPolicy()) && StringUtils.isNotBlank(request.getPolicyOrgId())) {
                        resident.setUid(request.getPolicy());
                        resident.setOrgId(request.getPolicyOrgId());
                    } else {
                        resident.setUid(userInfo.getUid());
                        resident.setOrgId(userInfo.getOrgId());
                    }
                }
            }
            log.info("getAllDifferentialPrice rooms={}", JsonUtils.toJsonString(request.getRooms()));
            for (GetAllDifferentialPriceRequest.Room room : request.getRooms()) {
                GetDifferentialPriceRequest getDifferentialPriceRequest = this.checkRoomRequestData(request, room);
                CompletableFuture<GetDifferentialPriceResponse> future = CompletableFuture.supplyAsync(() -> this.getDifferentialPrice(getDifferentialPriceRequest, userInfo), defaultThreadPoolExecutor);
                futures.add(future);
            }
            List<GetDifferentialPriceResponse> roomDifferentialPrice = BaseUtils.getFuture("同住差标执行", 2L, futures);
            addElkInfoLog("获取到每间房间的同住差标数据 %s", JsonUtils.toJsonString(roomDifferentialPrice));

            return hotelSharingData(roomDifferentialPrice, request, userInfo);
        } finally {
            log.info("多房间多人同住差标计算:{} ", getElkInfoLog());
            clearElkLog();
        }
    }

    private GetDifferentialPriceRequest checkRoomRequestData(GetAllDifferentialPriceRequest request, GetAllDifferentialPriceRequest.Room room) {
        GetDifferentialPriceRequest getDifferentialPriceRequest = new GetDifferentialPriceRequest();
        getDifferentialPriceRequest.setCityId(request.getCityId());
        getDifferentialPriceRequest.setEDate(request.getEDate());
        getDifferentialPriceRequest.setPolicy(request.getPolicy());
        getDifferentialPriceRequest.setPolicyOrgId(request.getPolicyOrgId());
        getDifferentialPriceRequest.setSDate(request.getSDate());

        getDifferentialPriceRequest.setRoomNumber(room.getRoomNumber());
        getDifferentialPriceRequest.setResidentList(room.getResidentList());

        return getDifferentialPriceRequest;
    }


    /**
     * 	处理酒店同住数据
     *
     * @param
     * @param roomDifferentialPrice
     */
    private HotelControlVo hotelSharingData(List<GetDifferentialPriceResponse> roomDifferentialPrice,
                                            GetAllDifferentialPriceRequest request,
                                            BaseUserInfo userInfo) {
        HotelControlVo hotelControl = request.getHotelControl();
        double priceCeiling = 0d;
        double priceFloor = 0d;
        if (roomDifferentialPrice.stream().filter(r -> r.getIsLimit()).count() > 0) {
            priceCeiling = calculatePriceToOrder(request, userInfo, roomDifferentialPrice.stream().map(GetDifferentialPriceResponse::getPriceCeiling).collect(Collectors.toList()));
            priceFloor = calculatePriceToOrder(request, userInfo, roomDifferentialPrice.stream().map(GetDifferentialPriceResponse::getPriceFloor).collect(Collectors.toList()));
        }
        //取所有房间  差标最低得价格进行房间得筛选
        int starCeiling = 0;
        if (roomDifferentialPrice.stream().filter(a -> Objects.equals("0", a.getStarCeiling())).count() == 0) {
            starCeiling = roomDifferentialPrice.stream().mapToInt(a -> Integer.valueOf(a.getStarCeiling())).max().getAsInt();
        }
        int starFloor = roomDifferentialPrice.stream().mapToInt(a -> Integer.valueOf(a.getStarFloor())).min().getAsInt();

        StarCeiling starCeilingDTO = new StarCeiling();
        starCeilingDTO.setValue(String.valueOf(starCeiling));
        StarFloor starFloorDTO = new StarFloor();
        starFloorDTO.setValue(String.valueOf(starFloor));

        AveragePriceSet averagePriceSet = new AveragePriceSet();
        averagePriceSet.setPriceCeiling(String.valueOf(priceCeiling));
        averagePriceSet.setPriceFloor(String.valueOf(priceFloor));

        AverageStarSet averageStarSet = new AverageStarSet();
        averageStarSet.setStarCeiling(starCeilingDTO);
        averageStarSet.setStarFloor(starFloorDTO);
        hotelControl.setAveragePriceSet(averagePriceSet);
        hotelControl.setAverageStarSet(averageStarSet);
        log.info("计算完同住差标后的数据：{}", JsonUtils.toJsonString(hotelControl));
        getFloatAveragePriceSet(hotelControl, request, userInfo);
        return hotelControl;
    }

    private void getFloatAveragePriceSet(HotelControlVo hotelControl, GetAllDifferentialPriceRequest request, BaseUserInfo userInfo) {
        MbTravelstandHotelVo hotelSwitch = null;
        if (StringUtils.isEmpty(request.getPolicy()) || StringUtils.isEmpty(request.getPolicyOrgId())) {
            hotelSwitch = travelstandHotelService.getHotelSwitch(userInfo.getUid(), userInfo.getOrgId());
        } else {
            hotelSwitch = travelstandHotelService.getHotelSwitch(request.getPolicy(), request.getPolicyOrgId());
        }
        if (!Objects.equals(HotelSwitchEnum.priceSwitch.E.getCode(), hotelSwitch.getPriceFloatSwitch())) {
            return;
        }

        String corpId = getCorpIdByOrgId(StringUtils.isEmpty(request.getPolicyOrgId()) ? userInfo.getOrgId() : request.getPolicyOrgId());
        List<AreaConfigVo> areaConfigVos = areaConfigService.getByOrgIdAndTsIdAndCityCode(corpId, request.getCityId(), hotelSwitch.getId());

        if (CollectionUtils.isEmpty(areaConfigVos) || areaConfigVos.size() > 1) {
            // 区域没有配置，或者找到多条
            return;
        }
        AreaConfigVo areaConfigVo = areaConfigVos.get(0);
        MbTravelstandHotelVo finalHotelSwitch = hotelSwitch;
        List<MbTravelstandHotelPriceVo> mbTravelstandHotelPriceVos = travelstandHotelPriceService.list(new MbTravelstandHotelPriceVo() {{
            setTsHotelId(finalHotelSwitch.getId());
            setTsHotelAreaId(areaConfigVo.getId());
        }});

        if (StringUtils.isEmpty(mbTravelstandHotelPriceVos.get(0).getFloatType())) {
            return;
        }
        AveragePriceSet averagePriceSet = new AveragePriceSet();
        log.info("同住查询浮动差标百分比：{}", JsonUtils.toJsonString(mbTravelstandHotelPriceVos.get(0)));
        //标准差标价格
        String priceCeiling = request.getHotelControl().getAveragePriceSet().getPriceCeiling();
        if (Objects.equals(HotelTravelStanderFloatTypeEnum.PERCENTAGE.getCode(), mbTravelstandHotelPriceVos.get(0).getFloatType())) {
            averagePriceSet.setPriceCeiling(new BigDecimal(priceCeiling).add(
                    new BigDecimal(priceCeiling).multiply((
                            Optional.ofNullable(mbTravelstandHotelPriceVos.get(0).getFloatPercentage()).orElse(BigDecimal.ZERO)
                                    .divide(new BigDecimal(100))))
            ).toString());
        } else {
            averagePriceSet.setPriceCeiling(new BigDecimal(priceCeiling).add(
                    Optional.ofNullable(mbTravelstandHotelPriceVos.get(0).getFloatAmount()).orElse(BigDecimal.ZERO)
            ).toString());
        }
        hotelControl.setFloatAveragePriceSet(averagePriceSet);
        hotelControl.setFloatControl(getControl(finalHotelSwitch.getFloatControlType()));
    }

    /**
     * 传入的orgId不确定为部门id或者公司id，这里通过组织架构接口，传入组织id，如果该组织是公司，则返回该id，如果不是，则返回最近的公司id
     * <p>
     * 酒店的地区信息是挂在公司下面的，而不是部门下面，查差标需要用到部门id，而查地区信息则需要公司id，故需要转换
     */
    private String getCorpIdByOrgId(String orgId) {
        MbOrgInfo orgInfoVo = organizationInfoService.findRecentCompanyIdByOrgId(orgId);
        if (orgInfoVo != null) {
            return orgInfoVo.getOrgId();
        }
        return null;
    }

    private Integer getControl(String floatControlType) {
        if (ControlTypeEnum.F.getCode().equals(floatControlType)) {
            return ControlTypeEnum.F.getValue();
        } else if (ControlTypeEnum.C.getCode().equals(floatControlType)) {
            return ControlTypeEnum.C.getValue();
        } else if (ControlTypeEnum.A.getCode().equals(floatControlType)) {
            return ControlTypeEnum.A.getValue();
        } else if (ControlTypeEnum.M.getCode().equals(floatControlType)) {
            return ControlTypeEnum.M.getValue();
        } else {
            return ControlTypeEnum.W.getValue();
        }
    }

    /**
     * 按照订单管控计算差标价格
     * @return
     */
    private double calculatePriceToOrder(GetAllDifferentialPriceRequest request, BaseUserInfo userInfo, List<String> priceList) {
        //重新获取一下酒店差标
        //如果差标就低，排除差标金额为零的如果还有其他数据，选金额最小的，如果没有的话，就选0，差标就高，如果金额存在0就选0（0代表不限）
        MbTravelstandHotelVo hotelSwitch = null;
        if (StringUtils.isEmpty(request.getPolicy()) || StringUtils.isEmpty(request.getPolicyOrgId())) {
            hotelSwitch = travelstandHotelService.getHotelSwitch(userInfo.getUid(), userInfo.getOrgId());
        } else {
            hotelSwitch = travelstandHotelService.getHotelSwitch(request.getPolicy(), request.getPolicyOrgId());
        }
        log.info("查询同住订单管控规则：{}", JsonUtils.toJsonString(hotelSwitch));
//        if (HotelManageStrategyEnum.ROOM.getCode().equals(hotelSwitch.getHotelManageStrategy())) {
//            calculatePriceToRoom(priceList);
//        }
        switch (HotelManageRulesEnum.getValue(hotelSwitch.getHotelManageRules())) {
            case LOW:
                if (priceList.stream().filter(hotelControlVo -> "0".equals(hotelControlVo)).count() > 0) {
                    return priceList.stream().filter(price -> !"0".equals(price)).count() == 0 ? 0d : priceList.stream().filter(price -> !"0".equals(price)).mapToDouble(price -> Double.valueOf(price)).min().getAsDouble();
                }
                return priceList.stream().mapToDouble(price -> Double.valueOf(price)).min().getAsDouble();
            case TALL:
                if (priceList.stream().filter(price -> "0".equals(price)).count() > 0) {
                    return 0d;
                }
                return priceList.stream().mapToDouble(price -> Double.valueOf(price)).max().getAsDouble();
            case SUM:
                if (priceList.stream().filter(price -> "0".equals(price)).count() > 0) {
                    return 0d;
                }
                return priceList.stream().mapToDouble(price -> Double.valueOf(price)).sum();
            default:
                return 0d;
        }
    }

    private double calculatePriceToRoom(List<String> priceList) {
        return priceList.stream().mapToDouble(a -> Double.valueOf(a)).min().getAsDouble();
    }
}
